package com.yesun.task.pipeline.handler.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yesun.core.util.FileUtils;
import com.yesun.server.mdp.service.client.config.HttpConfig;
import com.yesun.server.mdp.service.client.impl.DefaultClient;
import com.yesun.server.mdp.service.client.impl.http.HttpRequestContext;
import com.yesun.server.mdp.service.client.impl.http.HttpResponseContext;
import com.yesun.task.pipeline.common.Constant;
import com.yesun.task.pipeline.common.StockCode;
import com.yesun.task.pipeline.domain.dto.JobDto;
import com.yesun.task.pipeline.domain.dto.TaskDto;
import com.yesun.task.pipeline.handler.Executor;
import com.yesun.task.pipeline.handler.Handler;
import com.yesun.task.pipeline.handler.Loader;
import com.yesun.task.pipeline.worker.ParallelWorker;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.BlockingQueue;

//基础数据Handler
@Slf4j
public class BaseHandler extends Handler {

    static DefaultClient client = new DefaultClient();
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    static SimpleDateFormat shortSdf = new SimpleDateFormat("yyyyMMdd");
    static String TODAY = shortSdf.format(Calendar.getInstance().getTime());
    static Loader loader;

    public BaseHandler(JobDto jobDto) {
        super(jobDto);
    }

    @Override
    public Loader getLoader() {
        //实例化
        return new Loader() {
            @Override
            public void load(BlockingQueue queue) {
                log.info("加载Base数据采集任务...");
                int pagesize = 6000; //一次性取出所有数据
                //http://55.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240523801592311929_1692314671179&pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&wbp2u=|0|0|0|web&fid=f3&fs=m:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23,m:0+t:81+s:2048&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152&_=1692314671197
                String url = "http://55.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240523801592311929_1692314671179&pn=1&pz="+pagesize+"&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&wbp2u=%7C0%7C0%7C0%7Cweb&fid=f3&fs=m:0+t:6%2Cm:0+t:80%2Cm:1+t:2%2Cm:1+t:23%2Cm:0+t:81+s:2048&fields=f1%2Cf2%2Cf3%2Cf4%2Cf5%2Cf6%2Cf7%2Cf8%2Cf9%2Cf10%2Cf12%2Cf13%2Cf14%2Cf15%2Cf16%2Cf17%2Cf18%2Cf20%2Cf21%2Cf23%2Cf24%2Cf25%2Cf22%2Cf11%2Cf62%2Cf128%2Cf136%2Cf115%2Cf152&_=1692314671197";
                log.info("url:"+url);
                try {
                    StringBuffer stringBuffer = new StringBuffer();
                    CloseableHttpClient httpclient = HttpClients.createDefault();
                    CloseableHttpResponse response = null;
                    HttpGet httpGet = new HttpGet(url);
                    try {
                        response = httpclient.execute(httpGet);
                        HttpEntity entity = response.getEntity();
                        Header[] headers = response.getAllHeaders();
                        if (entity != null) {
                            InputStream inputStream = entity.getContent();
                            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                            String line;
                            while ((line = reader.readLine()) != null) {
                                stringBuffer.append(line);
                            }
                        }
                    } catch (org.apache.http.client.ClientProtocolException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (response != null) {
                            try {
                                response.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    String content = stringBuffer.toString();
                    content = content.substring(content.indexOf("{"), content.lastIndexOf("}") + 1);
                    log.info("content:" + content);

                    /** 数据结构
                     jQuery11240523801592311929_1692314671179(
                     {"rc":0,"rt":6,"svr":180606324,"lt":1,"full":1,"dlmkts":"",
                     "data":{"total":5500,"diff":[
                               1最新价    2涨跌幅     3涨跌额	  4成交量(手)  5成交额	      6振幅	      7换手率    8市盈率(动态)   9liangbi              11股票代码               13股票名称      14最高	  15最低	      16今开	     17昨收     18总市值           19流通市值                  21市净率                                            25市盈率TTM
                     {"f1":2,"f2":41.7,"f3":76.1,"f4":18.02,"f5":176024,"f6":730913662.0,"f7":46.88,"f8":90.17,"f9":-1098.31,"f10":"-","f11":-0.79,"f12":"688573","f13":1,"f14":"N信宇人","f15":47.86,"f16":36.76,"f17":42.0,"f18":23.68,"f20":4076357980,"f21":814080786,"f22":0.0,"f23":4.66,"f24":76.1,"f25":76.1,"f62":283658627.0,"f115":40.41,"f128":"-","f140":"-","f141":"-","f136":"-","f152":2},
                     {"f1":2,"f2":43.84,"f3":56.57,"f4":15.84,"f5":460380,"f6":2166614579.64,"f7":43.46,"f8":79.17,"f9":121.21,"f10":"-","f11":-0.14,"f12":"301511","f13":0,"f14":"N德福","f15":56.0,"f16":43.83,"f17":46.8,"f18":28.0,"f20":19738083200,"f21":2549414938,"f22":0.0,"f23":4.73,"f24":56.57,"f25":56.57,"f62":196196473.0,"f115":46.87,"f128":"-","f140":"-","f141":"-","f136":"-","f152":2},
                     **/
                    JSONArray jsonArray = JSONObject.parseObject(content)
                            .getJSONObject("data")
                            .getJSONArray("diff");
                    jsonArray.stream()
                            .forEach(line -> {
                                //遍历每一条记录
                                JSONObject obj = (JSONObject) line;
                                //f12,f14
                                String code = obj.getString("f12");
                                String stockName = obj.getString("f14");

                                TaskDto taskDto = new TaskDto();
                                taskDto.setJobName(jobDto.getJobName());
                                taskDto.setName(code + " Base数据采集任务");
                                taskDto.setTag(Constant.JOB_TAG_BASE);

                                Map<String, String> map = new HashMap<>();
                                map.put("url", "http://q.stock.sohu.com/cn/"+code+"/index.shtml");
                                map.put("code", code);
                                map.put("price", obj.getString("f2"));
                                //读取动态市盈率，基于当前价格计算
                                map.put("pe", obj.getString("f9"));
                                map.put("pe2", obj.getString("f23")); //市净率
                                map.put("pe_ttm", obj.getString("f115"));
                                map.put("stockName", stockName);
                                map.put("saveTo", "/df_base/"+shortSdf.format(new Date())+"/" + code + ".csv");//暂时只支持保存到磁盘
                                taskDto.setMap(map);
                                queue.offer(taskDto);
                                log.info("添加子任务：" + taskDto.getName());

                                //加入MAP_TASK
                                ParallelWorker.MAP_TASK.put(taskDto.getName(), taskDto.getJobName());

                            });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
    }

    private static String getText(Node element) {
        if(element == null)
            return  "";
        String str = element.asXML().replaceAll("<[^>]+>", "");
        str = str.replaceAll("(\\r\\n)*", "");
        str = str.replaceAll("(\\n)*", "");
        str = str.replaceAll("(\\s)*", "");
        str = str.replaceAll("(\\t)*", "");
        str = str.replace(",", ";");
        str = str.replace("收起", "");
        str = str.replace("　　", "");
        str = str.replace("\"", "");//引号会导致csv格式错误
        return str;
    }

    @Override
    public Executor getExecutor() {
        return new Executor() {
            @Override
            public void execute(TaskDto taskDto) {
                //执行task的采集
                log.info("execute：" + taskDto.getName());

                String code = taskDto.getMap().get("code");

                HttpConfig config = new HttpConfig(taskDto.getMap().get("url"), HttpConfig.HTTP_METHOD_GET);
                config.setEnctype("");
                HttpRequestContext request = new HttpRequestContext(config);
                try {
                    HttpResponseContext response = (HttpResponseContext) client.execute(request);

                    Document doc = response.getContentAsDocument();
                    StockHolder stockHolder = new StockHolder();
                    stockHolder.setDate(shortSdf.format(Calendar.getInstance().getTime()));
                    stockHolder.setStockCode(code);
                    stockHolder.setStockName(taskDto.getMap().get("stockName"));
                    stockHolder.setStockPrice(taskDto.getMap().get("price"));
//                    stockHolder.setPe(page.getPe()); 暂时取消

                    //个股概况
                    Element element = (Element) doc.selectSingleNode("//table[@class=\"table table03\"]");
                    if(element != null) {
                        stockHolder.setTotalCapitalStock(element.selectSingleNode("tbody/tr[1]/td[2]")!=null?element.selectSingleNode("tbody/tr[1]/td[2]").getText():"0");
                        stockHolder.setFlowCapitalStock(element.selectSingleNode("tbody/tr[2]/td[2]")!=null?element.selectSingleNode("tbody/tr[2]/td[2]").getText():"0");
                        stockHolder.setRoe(element.selectSingleNode("tbody/tr[5]/td[2]")!=null?element.selectSingleNode("tbody/tr[5]/td[2]").getText():"0");
                        //TODO 需要其他的字段可以扩展
                    }

                    //公司基本信息 http://basic.10jqka.com.cn/mobile/000001/profilen.html
                    config = new HttpConfig("http://basic.10jqka.com.cn/mobile/"+ code +"/profilen.html", HttpConfig.HTTP_METHOD_GET);
                    config.setEnctype("");
                    request = new HttpRequestContext(config);
                    response = (HttpResponseContext) client.execute(request);
                    Document document = response.getContentAsDocument();

                    //公司名称
                    stockHolder.setCompanyName(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='公司名称']/td[1]")));
                    stockHolder.setAddress(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='注册地址']/td[1]")));
                    stockHolder.setChairman(getText(document.selectSingleNode("//div[@id=\"detailData\"]/div[2]/table/tbody/tr[4]/td[1]")));
                    stockHolder.setMainBusiness(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='主营业务']/td[1]")));
                    stockHolder.setController(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='控股股东']/td[1]")));
                    stockHolder.setRealController(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='实际控制人']/td[1]")));
                    //这里的xpath牛逼，取某个节点的子节点作为条件 /tr[th/text()='abc']表示取tr，但tr子节点text=abc作为条件
                    stockHolder.setEndController(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='最终控制人']/td[1]")));
                    stockHolder.setIpoDate(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='上市日期']/td[1]")));
                    stockHolder.setIpoPrice(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='发行价/市盈率']/td[1]")));
                    stockHolder.setEmployees(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='员工人数']/td[1]")));
                    stockHolder.setTel(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='联系电话']/td[1]")));
                    stockHolder.setWebsite(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='公司网址']/td[1]")));
                    stockHolder.setEmail(getText(document.selectSingleNode("//div[@id=\"detailData\"]//tr[th/text()='邮箱地址']/td[1]")));

                    stockHolder.setProductType(getText(document.selectSingleNode("//div[@id=\"mainIntro\"]/div[2]/table/tbody/tr[1]/td[1]")));
                    stockHolder.setProductName(getText(document.selectSingleNode("//div[@id=\"mainIntro\"]/div[2]/table/tbody/tr[2]/td[1]")));
                    stockHolder.setBusinessScope(getText(document.selectSingleNode("//div[@id=\"mainIntro\"]/div[2]/table/tbody/tr[3]/td[1]/div[@class=\"cont-long\"]")));

                    //http://basic.10jqka.com.cn/mobile/300624/companyn.html
                    config = new HttpConfig("http://basic.10jqka.com.cn/mobile/"+ code +"/companyn.html", HttpConfig.HTTP_METHOD_GET);
                    config.setEnctype("");
                    request = new HttpRequestContext(config);
                    response = (HttpResponseContext) client.execute(request);
                    document = response.getContentAsDocument();
                    stockHolder.setArea(getText(document.selectSingleNode("//div[@id=\"companyn_profile\"]//tr[th/text()='所属区域']/td[1]")));
                    stockHolder.setIndustry(getText(document.selectSingleNode("//div[@id=\"companyn_profile\"]//tr[th/text()='所属行业']/td[1]")));

                    //题材概念
                    Element elemTheme = (Element) document.selectSingleNode("//a[@tar_name=\"概念题材\"]/div[2]");
                    if(elemTheme!=null) {
                        StringBuffer sbTheme = new StringBuffer();
                        //遍历概念
                        List<Element> list = elemTheme.selectNodes("ul/li");
                        for(Element elem : list) {
                            sbTheme.append(getText(elem)+"|");
                        }
                        stockHolder.setTheme(sbTheme.toString());
                    }
                    stockHolder.setPe(taskDto.getMap().get("pe"));
                    stockHolder.setPe2(taskDto.getMap().get("pe2"));
                    stockHolder.setPe_ttm(taskDto.getMap().get("pe_ttm"));

                    StringBuffer sb = new StringBuffer();
                    sb.append("采集日期,股票代码,股票名称,公司名称,办公地址,董事长,主营业务,控股股东,实际控制人,最终控制人,首次上市,发行价,员工数量,电话,公司网址,公司邮件,产品类型,产品名称,业务范围,总股本,流通股本,题材概念,所属区域,所属行业,市盈率,净资产收益率,股票价格,市净率,市盈率TTM\r\n");
                    sb.append(stockHolder.toString());
                    //save csv
                    FileUtils.saveCsv(Constant.BASE_PATH + taskDto.getMap().get("saveTo"), sb.toString(), "UTF-8");

                }catch (Exception e) {
                    e.printStackTrace();
                }

            }
        };
    }

    @Data
    private class StockHolder {
        //股东名称	持股数量(万股)	持股比例	持股变化(万股)	股本性质
        private String date;//采集日期
        private String stockCode;
        private String stockName;
        //基本信息
        private String companyName; //公司名称
        private String address;//办公地址
        private String chairman;//董 事 长
        private String mainBusiness; //主营业务
        private String controller;//控股股东
        private String realController;//实际控制人
        private String endController;//最终控制人
        private String ipoDate;//首次上市
        private String ipoPrice;//发行价
        private String employees;//员工数量
        private String tel;//电话
        private String website;//公司网址
        private String email;
        //主营业务介绍
        private String productType;//产品类型
        private String productName;//产品名称
        private String businessScope;//业务范围
        //其他信息
        private String totalCapitalStock;//总股本
        private String flowCapitalStock;//流通股本
        private String theme;//题材概念
        private String area;//所属区域
        private String industry;//所属行业
        //暂时冗余的信息，最好动态计算或动态获取
        private String pe;//市盈率，此时此刻的pe，具体分析时需要动态计算最新的；
        private String roe;//资产收益率；
        private String stockPrice;
        private String pe2;//市净率
        private String pe_ttm;//市盈率TTM


        public String toString() {
            return this.getDate() + ","
                    + this.getStockCode() + ","
                    + this.getStockName() + ","
                    + this.getCompanyName() + ","
                    + this.getAddress() + ","
                    + this.getChairman() + ","
                    + this.getMainBusiness() + ","
                    + this.getController() + ","
                    + this.getRealController() + ","
                    + this.getEndController() + ","
                    + this.getIpoDate() + ","
                    + this.getIpoPrice() + ","
                    + this.getEmployees() + ","
                    + this.getTel() + ","
                    + this.getWebsite() + ","
                    + this.getEmail() + ","
                    + this.getProductType() + ","
                    + this.getProductName() + ","
                    + this.getBusinessScope() + ","
                    + this.getTotalCapitalStock() + ","
                    + this.getFlowCapitalStock() + ","
                    + this.getTheme() + ","
                    + this.getArea() + ","
                    + this.getIndustry() + ","
                    + this.getPe() + ","
                    + this.getRoe() + ","
                    + this.getStockPrice() + ","
                    + this.getPe2() + ","
                    + this.getPe_ttm();
        }
    }
}
